/*
 * 执行 Python 代码
 *
 * src/pl/plpython/plpy_exec.c
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "plpy_elog.h"
#include "plpy_exec.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
#include "plpython.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/typcache.h"

/* 用于返回集合的函数的保存状态 */
typedef struct PLySRFState
{
	PyObject   *iter;			/* 生成结果的 Python 迭代器 */
	PLySavedArgs *savedargs;	/* 函数参数值 */
	MemoryContextCallback callback; /* 完成时释放引用计数 */
} PLySRFState;

static PyObject *fc_PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *fc_proc);
static PLySavedArgs *fc_PLy_function_save_args(PLyProcedure *fc_proc);
static void fc_PLy_function_restore_args(PLyProcedure *fc_proc, PLySavedArgs *fc_savedargs);
static void fc_PLy_function_drop_args(PLySavedArgs *fc_savedargs);
static void fc_PLy_global_args_push(PLyProcedure *fc_proc);
static void fc_PLy_global_args_pop(PLyProcedure *fc_proc);
static void fc_plpython_srf_cleanup_callback(void *fc_arg);
static void fc_plpython_return_error_callback(void *fc_arg);

static PyObject *fc_PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *fc_proc,
										HeapTuple *fc_rv);
static HeapTuple fc_PLy_modify_tuple(PLyProcedure *fc_proc, PyObject *fc_pltd,
								  TriggerData *fc_tdata, HeapTuple fc_otup);
static void fc_plpython_trigger_error_callback(void *fc_arg);

static PyObject *fc_PLy_procedure_call(PLyProcedure *fc_proc, const char *fc_kargs, PyObject *fc_vargs);
static void fc_PLy_abort_open_subtransactions(int fc_save_subxact_level);


/* 函数子处理程序 */
Datum PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *fc_proc)
{
	bool		fc_is_setof = fc_proc->is_setof;
	Datum		fc_rv;
	PyObject   *volatile fc_plargs = NULL;
	PyObject   *volatile fc_plrv = NULL;
	FuncCallContext *volatile fc_funcctx = NULL;
	PLySRFState *volatile fc_srfstate = NULL;
	ErrorContextCallback fc_plerrcontext;

	/*
	 * 如果函数被递归调用，我们必须将外层
	 * 参数推入栈中。这必须在 PG_TRY 之前立即进行
	 * 以确保相应的弹出操作发生。
	 */
	fc_PLy_global_args_push(fc_proc);

	PG_TRY();
	{
		if (fc_is_setof)
		{
			/* 第一次调用设置 */
			if (SRF_IS_FIRSTCALL())
			{
				fc_funcctx = SRF_FIRSTCALL_INIT();
				fc_srfstate = (PLySRFState *)
					MemoryContextAllocZero(fc_funcctx->multi_call_memory_ctx,
										   sizeof(PLySRFState));
				/* 立即注册清理回调 */
				fc_srfstate->callback.func = fc_plpython_srf_cleanup_callback;
				fc_srfstate->callback.arg = (void *) fc_srfstate;
				MemoryContextRegisterResetCallback(fc_funcctx->multi_call_memory_ctx,
												   &fc_srfstate->callback);
				fc_funcctx->user_fctx = (void *) fc_srfstate;
			}
			/* 每次调用设置 */
			fc_funcctx = SRF_PERCALL_SETUP();
			Assert(fc_funcctx != NULL);
			fc_srfstate = (PLySRFState *) fc_funcctx->user_fctx;
			Assert(fc_srfstate != NULL);
		}

		if (fc_srfstate == NULL || fc_srfstate->iter == NULL)
		{
			/*
			 * 非 SETOF 函数或 SETOF 函数的第一次调用：构建
			 * 参数，然后实际执行函数。
			 */
			fc_plargs = fc_PLy_function_build_args(fcinfo, fc_proc);
			fc_plrv = fc_PLy_procedure_call(fc_proc, "args", fc_plargs);
			Assert(fc_plrv != NULL);
		}
		else
		{
			/*
			 * SETOF 函数的第二次或随后的调用：恢复全局字典中的
			 * 参数到我们离开时的状态。我们必须这样做，以防
			 * 多次评估同一 SETOF 函数交错进行。这有点烦人，
			 * 因为迭代器可能根本不查看参数，但我们无法知道
			 * 这一点。幸运的是，这并不是特别昂贵。
			 */
			if (fc_srfstate->savedargs)
				fc_PLy_function_restore_args(fc_proc, fc_srfstate->savedargs);
			fc_srfstate->savedargs = NULL; /* 被 restore_args 删除 */
		}

		/*
		 * 如果它返回一个集合，调用迭代器以获取下一个返回项。
		 * 在执行此操作时，我们保持在 SPI 上下文中，
		 * 因为 PyIter_Next() 会回调 Python 代码，可能包含 SPI 调用。
		 */
		if (fc_is_setof)
		{
			if (fc_srfstate->iter == NULL)
			{
				/* 第一次 -- 进行检查和设置 */
				ReturnSetInfo *fc_rsi = (ReturnSetInfo *) fcinfo->resultinfo;

				if (!fc_rsi || !IsA(fc_rsi, ReturnSetInfo) ||
					(fc_rsi->allowedModes & SFRM_ValuePerCall) == 0)
				{
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("unsupported set function return mode"),
							 errdetail("PL/Python set-returning functions only support returning one value per call.")));
				}
				fc_rsi->returnMode = SFRM_ValuePerCall;

				/* 从返回对象创建迭代器 */
				fc_srfstate->iter = PyObject_GetIter(fc_plrv);

				Py_DECREF(fc_plrv);
				fc_plrv = NULL;

				if (fc_srfstate->iter == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("returned object cannot be iterated"),
							 errdetail("PL/Python set-returning functions must return an iterable object.")));
			}

			/* 从迭代器中获取下一个 */
			fc_plrv = PyIter_Next(fc_srfstate->iter);
			if (fc_plrv == NULL)
			{
				/* 迭代器耗尽或发生错误 */
				bool		fc_has_error = (PyErr_Occurred() != NULL);

				Py_DECREF(fc_srfstate->iter);
				fc_srfstate->iter = NULL;

				if (fc_has_error)
					PLy_elog(ERROR, "error fetching next item from iterator");

				/* 在下面的数据返回步骤中传递 null */
				Py_INCREF(Py_None);
				fc_plrv = Py_None;
			}
			else
			{
				/*
				 * 这不会是最后一次调用，所以保存参数值。我们在
				 * 每次都这样做，以防迭代器正在更改这些
				 * 值。
				 */
				fc_srfstate->savedargs = fc_PLy_function_save_args(fc_proc);
			}
		}

		/*
		 * 断开与 SPI 管理器的连接，然后创建返回值数据项
		 * （如果输入函数为其执行 palloc，这必须在
		 * SPI 的内存上下文中分配，因为 SPI_finish 会释放
		 * 它）。
		 */
		if (SPI_finish() != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed");

		fc_plerrcontext.callback = fc_plpython_return_error_callback;
		fc_plerrcontext.previous = error_context_stack;
		error_context_stack = &fc_plerrcontext;

		/*
		 * 对于声明为返回 void 的过程或函数，Python
		 * 返回值必须为 None。对于返回 void 的函数，
		 * 我们也将 None 返回值视为特殊的 “void datum”，而不是
		 * NULL（这与非 void 返回函数的情况不同）。
		 */
		if (fc_proc->result.typoid == VOIDOID)
		{
			if (fc_plrv != Py_None)
			{
				if (fc_proc->is_procedure)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("PL/Python procedure did not return None")));
				else
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("PL/Python function with return type \"void\" did not return None")));
			}

			fcinfo->isnull = false;
			fc_rv = (Datum) 0;
		}
		else if (fc_plrv == Py_None &&
				 fc_srfstate && fc_srfstate->iter == NULL)
		{
			/*
			 * 在 SETOF 函数中，迭代结束的 null 不是一个真实
			 * 值；不要将其传递给输入函数，可能会抱怨。
			 */
			fcinfo->isnull = true;
			fc_rv = (Datum) 0;
		}
		else
		{
			/*
			 * 正常的结果转换。然而，如果结果是 RECORD 类型，
			 * 我们必须每次都进行设置，因为它可能与上次不同。
			 */
			if (fc_proc->result.typoid == RECORDOID)
			{
				TupleDesc	fc_desc;

				if (get_call_result_type(fcinfo, NULL, &fc_desc) != TYPEFUNC_COMPOSITE)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("function returning record called in context "
									"that cannot accept type record")));
				PLy_output_setup_record(&fc_proc->result, fc_desc, fc_proc);
			}

			fc_rv = PLy_output_convert(&fc_proc->result, fc_plrv,
									&fcinfo->isnull);
		}
	}
	PG_CATCH();
	{
		/* 如果旧参数已推入栈中，则从栈中弹出 */
		fc_PLy_global_args_pop(fc_proc);

		Py_XDECREF(fc_plargs);
		Py_XDECREF(fc_plrv);

		/*
		 * 如果在 SRF 中发生错误，迭代器可能尚未耗尽。
		 * 清除它，以便下次调用函数时重新开始迭代。 （这段代码现在可能
		 * 不再必要；plpython_srf_cleanup_callback 应该负责清理。
		 * 但这里执行也没有坏处。）
		 */
		if (fc_srfstate)
		{
			Py_XDECREF(fc_srfstate->iter);
			fc_srfstate->iter = NULL;
			/* 并丢弃任何保留的参数；我们不需要它们 */
			if (fc_srfstate->savedargs)
				fc_PLy_function_drop_args(fc_srfstate->savedargs);
			fc_srfstate->savedargs = NULL;
		}

		PG_RE_THROW();
	}
	PG_END_TRY();

	error_context_stack = fc_plerrcontext.previous;

	/* 如果旧参数已推入栈中，则从栈中弹出 */
	fc_PLy_global_args_pop(fc_proc);

	Py_XDECREF(fc_plargs);
	Py_DECREF(fc_plrv);

	if (fc_srfstate)
	{
		/* 我们在一个 SRF 中，适当地退出 */
		if (fc_srfstate->iter == NULL)
		{
			/* 迭代器已耗尽，因此我们完成了 */
			SRF_RETURN_DONE(fc_funcctx);
		}
		else if (fcinfo->isnull)
			SRF_RETURN_NEXT_NULL(fc_funcctx);
		else
			SRF_RETURN_NEXT(fc_funcctx, fc_rv);
	}

	/* 普通函数，仅返回 Datum 值（可能为 null） */
	return fc_rv;
}

/* 触发器子处理
 *
 * 如果元组是可接受且未修改的，预计 python 函数将返回 Py_None。
 * 否则，它应该返回一个值为 SKIP 或 MODIFY 的 PyUnicode 对象。
 * SKIP 意味着不执行该操作。 MODIFY 意味着元组已被修改，因此更新
 * 元组并执行操作。 SKIP 和 MODIFY 假设触发器在事件之前触发并且是行级的。
 * postgres 期望该函数不接受任何参数并返回类型为触发器的参数。
 */
HeapTuple PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *fc_proc)
{
	HeapTuple	fc_rv = NULL;
	PyObject   *volatile fc_plargs = NULL;
	PyObject   *volatile fc_plrv = NULL;
	TriggerData *fc_tdata;
	TupleDesc	fc_rel_descr;

	Assert(CALLED_AS_TRIGGER(fcinfo));
	fc_tdata = (TriggerData *) fcinfo->context;

	/*
	 * 触发器元组的输入/输出转换。 我们使用结果和
	 * result_in 字段来存储元组转换信息。 在每次调用时，我们都这样做
	 * 以覆盖触发器上次调用后关系的 tupdesc 可能已更改的可能性。
	 * PLy_xxx_setup_func 调用应该只发生一次，但 PLy_input_setup_tuple 和
	 * PLy_output_setup_tuple 负责不做重复工作。
	 */
	fc_rel_descr = RelationGetDescr(fc_tdata->tg_relation);
	if (fc_proc->result.typoid != fc_rel_descr->tdtypeid)
		PLy_output_setup_func(&fc_proc->result, fc_proc->mcxt,
							  fc_rel_descr->tdtypeid,
							  fc_rel_descr->tdtypmod,
							  fc_proc);
	if (fc_proc->result_in.typoid != fc_rel_descr->tdtypeid)
		PLy_input_setup_func(&fc_proc->result_in, fc_proc->mcxt,
							 fc_rel_descr->tdtypeid,
							 fc_rel_descr->tdtypmod,
							 fc_proc);
	PLy_output_setup_tuple(&fc_proc->result, fc_rel_descr, fc_proc);
	PLy_input_setup_tuple(&fc_proc->result_in, fc_rel_descr, fc_proc);

	/*
	 * 如果触发器被递归调用，我们必须将外层参数推入栈中。
	 * 这必须在 PG_TRY 之前立即进行，以确保相应的弹出发生。
	 */
	fc_PLy_global_args_push(fc_proc);

	PG_TRY();
	{
		int			fc_rc PG_USED_FOR_ASSERTS_ONLY;

		fc_rc = SPI_register_trigger_data(fc_tdata);
		Assert(fc_rc >= 0);

		fc_plargs = fc_PLy_trigger_build_args(fcinfo, fc_proc, &fc_rv);
		fc_plrv = fc_PLy_procedure_call(fc_proc, "TD", fc_plargs);

		Assert(fc_plrv != NULL);

		/*
		 * 与 SPI 管理器断开连接
		 */
		if (SPI_finish() != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed");

		/*
		 * 返回 None 表示我们对元组感到满意
		 */
		if (fc_plrv != Py_None)
		{
			char	   *fc_srv;

			if (PyUnicode_Check(fc_plrv))
				fc_srv = PLyUnicode_AsString(fc_plrv);
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATA_EXCEPTION),
						 errmsg("unexpected return value from trigger procedure"),
						 errdetail("Expected None or a string.")));
				fc_srv = NULL;		/* 保持编译器安静 */
			}

			if (pg_strcasecmp(fc_srv, "SKIP") == 0)
				fc_rv = NULL;
			else if (pg_strcasecmp(fc_srv, "MODIFY") == 0)
			{
				TriggerData *fc_tdata = (TriggerData *) fcinfo->context;

				if (TRIGGER_FIRED_BY_INSERT(fc_tdata->tg_event) ||
					TRIGGER_FIRED_BY_UPDATE(fc_tdata->tg_event))
					fc_rv = fc_PLy_modify_tuple(fc_proc, fc_plargs, fc_tdata, fc_rv);
				else
					ereport(WARNING,
							(errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored")));
			}
			else if (pg_strcasecmp(fc_srv, "OK") != 0)
			{
				/*
				 * 接受 "OK" 作为 None 的替代；否则，抛出一个
				 * 错误
				 */
				ereport(ERROR,
						(errcode(ERRCODE_DATA_EXCEPTION),
						 errmsg("unexpected return value from trigger procedure"),
						 errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\".")));
			}
		}
	}
	PG_FINALLY();
	{
		fc_PLy_global_args_pop(fc_proc);
		Py_XDECREF(fc_plargs);
		Py_XDECREF(fc_plrv);
	}
	PG_END_TRY();

	return fc_rv;
}

/* Python 代码执行的助手函数 */

static PyObject * fc_PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *fc_proc)
{
	PyObject   *volatile fc_arg = NULL;
	PyObject   *fc_args;
	int			fc_i;

	/*
	 * 在 PG_TRY 块之前进行任何 Py*_New() 调用，以便我们可以快速
	 * 在失败时返回 NULL。 我们不能在 PG_TRY 块内部返回，否则
	 * 我们将错过展开异常堆栈。
	 */
	fc_args = PyList_New(fc_proc->nargs);
	if (!fc_args)
		return NULL;

	PG_TRY();
	{
		for (fc_i = 0; fc_i < fc_proc->nargs; fc_i++)
		{
			PLyDatumToOb *fc_arginfo = &fc_proc->args[fc_i];

			if (fcinfo->args[fc_i].isnull)
				fc_arg = NULL;
			else
				fc_arg = PLy_input_convert(fc_arginfo, fcinfo->args[fc_i].value);

			if (fc_arg == NULL)
			{
				Py_INCREF(Py_None);
				fc_arg = Py_None;
			}

			if (PyList_SetItem(fc_args, fc_i, fc_arg) == -1)
				PLy_elog(ERROR, "PyList_SetItem() failed, while setting up arguments");

			if (fc_proc->argnames && fc_proc->argnames[fc_i] &&
				PyDict_SetItemString(fc_proc->globals, fc_proc->argnames[fc_i], fc_arg) == -1)
				PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments");
			fc_arg = NULL;
		}
	}
	PG_CATCH();
	{
		Py_XDECREF(fc_arg);
		Py_XDECREF(fc_args);

		PG_RE_THROW();
	}
	PG_END_TRY();

	return fc_args;
}

/*
 * 构造一个 PLySavedArgs 结构，表示该过程在其全局字典中
 * 参数的当前值。 这可以在退出递归调用级别或将控制权返回给
 * 集合返回函数时用于恢复这些值。
 *
 * 除了因为一个古老的决定而使得通过 proc 的全局变量提供 args 外，
 * 这将不是必需的 :-( ... 但这现在是我们必须接受的。
 */
static PLySavedArgs *
fc_PLy_function_save_args(PLyProcedure *fc_proc)
{
	PLySavedArgs *fc_result;

	/* 保存的参数始终在过程上下文中分配 */
	fc_result = (PLySavedArgs *)
		MemoryContextAllocZero(fc_proc->mcxt,
							   offsetof(PLySavedArgs, namedargs) +
							   fc_proc->nargs * sizeof(PyObject *));
	fc_result->nargs = fc_proc->nargs;

	/* 获取 "args" 列表 */
	fc_result->args = PyDict_GetItemString(fc_proc->globals, "args");
	Py_XINCREF(fc_result->args);

	/* 如果是触发器，另存 "TD" */
	if (fc_proc->is_trigger)
	{
		fc_result->td = PyDict_GetItemString(fc_proc->globals, "TD");
		Py_XINCREF(fc_result->td);
	}

	/* 获取所有命名参数 */
	if (fc_proc->argnames)
	{
		int			fc_i;

		for (fc_i = 0; fc_i < fc_result->nargs; fc_i++)
		{
			if (fc_proc->argnames[fc_i])
			{
				fc_result->namedargs[fc_i] = PyDict_GetItemString(fc_proc->globals,
															fc_proc->argnames[fc_i]);
				Py_XINCREF(fc_result->namedargs[fc_i]);
			}
		}
	}

	return fc_result;
}

/*
 * 从 PLySavedArgs 结构恢复过程的参数，
 * 然后释放该结构。
 */
static void fc_PLy_function_restore_args(PLyProcedure *fc_proc, PLySavedArgs *fc_savedargs)
{
	/* 将命名参数恢复到全局字典中的槽位 */
	/* 也恢复 "args" 对象 */
	/* 也恢复 "TD" 对象 */
	/* 并释放 PLySavedArgs 结构体 */
	/*
	 * 不恢复值而释放 PLySavedArgs 结构体。
 	*/
	/* 删除命名参数的引用 */
	/* 也删除对 "args" 和 "TD" 对象的引用 */
	/* 并释放 PLySavedArgs 结构体 */
	/*
 	* 保存给定过程的任何现有参数，以便我们可以
 	* 安装新的值以进行递归调用。在执行 PLy_function_build_args()
 	* 或 PLy_trigger_build_args() 之前应调用此函数。
 	*
 	* 注意：调用者必须确保在成功完成 PLy_global_args_push 时
 	* 仅调用 PLy_global_args_pop 一次。否则，我们将在
 	* 实际调用栈和 proc->argstack 的内容之间失去同步。
 	*/
	/* 我们只需要在已经进入某个活动调用时才推入 
	* 构建一个包含当前参数值的结构体 */
	/* 将保存的参数值推入过程的栈中。一旦我们
	 * 修改了 proc->argstack 或 proc->calldepth，我们最好
	 * 在没有可能出错的情况下返回。*/
	/*
 	* 在退出递归调用时弹出旧参数。
 	*
 	* 注意：这里的想法是在执行可能失败的任何操作之前
 	* 调整 proc 的调用栈状态。如果发生任何错误，我们希望调用栈
 	* 看起来像我们已经执行了弹出操作。泄漏一点内存是可以接受的。
 	*/
	/* 我们只需要在已经在某个活动调用时才弹出 */
	/* 弹出调用栈 */
	/* 恢复参数值，然后释放指针 */
	/* 退出调用深度 1 */
	/*
	 * 在退出函数的最外层调用级别时，我们以前会
	 * 从 proc 的全局字典中删除命名参数（但不包括 "args"）。
	 * 不过，这似乎没有什么意义：在函数再次被调用之前，
	 * 没有任何东西可以看到字典，此时我们将覆盖这些
	 * 字典条目。所以不要去打扰这个。
	 */
	/*
	 * 用于清理 PLySRFState 的内存上下文删除回调。
 	* 我们需要这个，以防 SRF 的执行提前终止，
 	* 由于错误或调用者根本没有完成它。
 	*/
	/* 如果我们仍然有迭代器，则释放引用计数 */
	/* 并删除任何保存的参数；我们不会需要它们 */
	/*
	 * 在 PG_TRY 块之前进行任何 Py*_New() 调用，以便我们可以快速
	 * 在失败时返回 NULL。我们不能在 PG_TRY 块内返回，否则
	 * 我们将错过展开异常堆栈。
	 */
	/* 保持编译器安静 */
	/*
	 * 注意：在 BEFORE 触发器中，存储的生成列尚未
	 * 被计算，因此不要在 NEW 行中使其可访问。
	 */
	/* 保持编译器安静 */
	/* 保持编译器安静 */
	/*
	 * 所有字符串...
	 */
	/* pltargs 应该在 PG_TRY 块之前分配。 */
	/*
	 * 被盗，不要 Py_DECREF
	 */
	if (fc_proc->argnames)
	{
		int			fc_i;

		for (fc_i = 0; fc_i < fc_savedargs->nargs; fc_i++)
		{
			if (fc_proc->argnames[fc_i] && fc_savedargs->namedargs[fc_i])
			{
				PyDict_SetItemString(fc_proc->globals, fc_proc->argnames[fc_i],
									 fc_savedargs->namedargs[fc_i]);
				Py_DECREF(fc_savedargs->namedargs[fc_i]);
			}
		}
	}

	/*
 * 应用由触发函数的 MODIFY 返回请求的更改。
 */
	if (fc_savedargs->args)
	{
		PyDict_SetItemString(fc_proc->globals, "args", fc_savedargs->args);
		Py_DECREF(fc_savedargs->args);
	}

	/* 保持编译器安静 */
	if (fc_savedargs->td)
	{
		PyDict_SetItemString(fc_proc->globals, "TD", fc_savedargs->td);
		Py_DECREF(fc_savedargs->td);
	}

	/* 我们假设 proc->result 已设置为正确转换元组 */
	pfree(fc_savedargs);
}

/* 执行 Python 代码，将 Python 错误传播到后端 */
static void fc_PLy_function_drop_args(PLySavedArgs *fc_savedargs)
{
	int			fc_i;

	/*
		 * 由于 plpy 只允许你关闭你
		 * 开始的子事务，你不能 *解除* 子事务，只能 *嵌套* 它们
		 * 而不关闭。
		 */
	for (fc_i = 0; fc_i < fc_savedargs->nargs; fc_i++)
	{
		Py_XDECREF(fc_savedargs->namedargs[fc_i]);
	}

	/* 如果 Python 代码返回了错误，传播它 */
	Py_XDECREF(fc_savedargs->args);
	Py_XDECREF(fc_savedargs->td);

	/* 我们假设 proc->result 已设置为正确转换元组 */
	pfree(fc_savedargs);
}

/*
 * Save away any existing arguments for the given procedure, so that we can
 * install new values for a recursive call.  This should be invoked before
 * doing PLy_function_build_args() or fc_PLy_trigger_build_args().
 *
 * NB: callers must ensure that PLy_global_args_pop gets invoked once, and
 * only once, per successful completion of PLy_global_args_push.  Otherwise
 * we'll end up out-of-sync between the actual call stack and the contents
 * of proc->argstack.
 */
static void fc_PLy_global_args_push(PLyProcedure *fc_proc)
{
	/* We only need to push if we are already inside some active call */
	if (fc_proc->calldepth > 0)
	{
		PLySavedArgs *fc_node;

		/* Build a struct containing current argument values */
		fc_node = fc_PLy_function_save_args(fc_proc);

		/*
		 * Push the saved argument values into the procedure's stack.  Once we
		 * modify either proc->argstack or proc->calldepth, we had better
		 * return without the possibility of error.
		 */
		fc_node->next = fc_proc->argstack;
		fc_proc->argstack = fc_node;
	}
	fc_proc->calldepth++;
}

/*
 * Pop old arguments when exiting a recursive call.
 *
 * Note: the idea here is to adjust the proc's callstack state before doing
 * anything that could possibly fail.  In event of any error, we want the
 * callstack to look like we've done the pop.  Leaking a bit of memory is
 * tolerable.
 */
static void fc_PLy_global_args_pop(PLyProcedure *fc_proc)
{
	Assert(fc_proc->calldepth > 0);
	/* We only need to pop if we were already inside some active call */
	if (fc_proc->calldepth > 1)
	{
		PLySavedArgs *fc_ptr = fc_proc->argstack;

		/* Pop the callstack */
		Assert(fc_ptr != NULL);
		fc_proc->argstack = fc_ptr->next;
		fc_proc->calldepth--;

		/* Restore argument values, then free ptr */
		fc_PLy_function_restore_args(fc_proc, fc_ptr);
	}
	else
	{
		/* Exiting call depth 1 */
		Assert(fc_proc->argstack == NULL);
		fc_proc->calldepth--;

		/*
		 * We used to delete the named arguments (but not "args") from the
		 * proc's globals dict when exiting the outermost call level for a
		 * function.  This seems rather pointless though: nothing can see the
		 * dict until the function is called again, at which time we'll
		 * overwrite those dict entries.  So don't bother with that.
		 */
	}
}

/*
 * Memory context deletion callback for cleaning up a PLySRFState.
 * We need this in case execution of the SRF is terminated early,
 * due to error or the caller simply not running it to completion.
 */
static void fc_plpython_srf_cleanup_callback(void *fc_arg)
{
	PLySRFState *fc_srfstate = (PLySRFState *) fc_arg;

	/* Release refcount on the iter, if we still have one */
	Py_XDECREF(fc_srfstate->iter);
	fc_srfstate->iter = NULL;
	/* 并丢弃任何保留的参数；我们不需要它们 */
	if (fc_srfstate->savedargs)
		fc_PLy_function_drop_args(fc_srfstate->savedargs);
	fc_srfstate->savedargs = NULL;
}

static void fc_plpython_return_error_callback(void *fc_arg)
{
	PLyExecutionContext *fc_exec_ctx = PLy_current_execution_context();

	if (fc_exec_ctx->curr_proc &&
		!fc_exec_ctx->curr_proc->is_procedure)
		errcontext("while creating return value");
}

static PyObject * fc_PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *fc_proc, HeapTuple *fc_rv)
{
	TriggerData *fc_tdata = (TriggerData *) fcinfo->context;
	TupleDesc	fc_rel_descr = RelationGetDescr(fc_tdata->tg_relation);
	PyObject   *fc_pltname,
			   *fc_pltevent,
			   *fc_pltwhen,
			   *fc_pltlevel,
			   *fc_pltrelid,
			   *fc_plttablename,
			   *fc_plttableschema,
			   *fc_pltargs,
			   *fc_pytnew,
			   *fc_pytold,
			   *fc_pltdata;
	char	   *fc_stroid;

	/*
	 * 在 PG_TRY 块之前进行任何 Py*_New() 调用，以便我们可以快速
	 * 在失败时返回 NULL。 我们不能在 PG_TRY 块内部返回，否则
	 * 我们将错过展开异常堆栈。
	 */
	fc_pltdata = PyDict_New();
	if (!fc_pltdata)
		return NULL;

	if (fc_tdata->tg_trigger->tgnargs)
	{
		fc_pltargs = PyList_New(fc_tdata->tg_trigger->tgnargs);
		if (!fc_pltargs)
		{
			Py_DECREF(fc_pltdata);
			return NULL;
		}
	}
	else
	{
		Py_INCREF(Py_None);
		fc_pltargs = Py_None;
	}

	PG_TRY();
	{
		fc_pltname = PLyUnicode_FromString(fc_tdata->tg_trigger->tgname);
		PyDict_SetItemString(fc_pltdata, "name", fc_pltname);
		Py_DECREF(fc_pltname);

		fc_stroid = DatumGetCString(DirectFunctionCall1(oidout,
													 ObjectIdGetDatum(fc_tdata->tg_relation->rd_id)));
		fc_pltrelid = PLyUnicode_FromString(fc_stroid);
		PyDict_SetItemString(fc_pltdata, "relid", fc_pltrelid);
		Py_DECREF(fc_pltrelid);
		pfree(fc_stroid);

		fc_stroid = SPI_getrelname(fc_tdata->tg_relation);
		fc_plttablename = PLyUnicode_FromString(fc_stroid);
		PyDict_SetItemString(fc_pltdata, "table_name", fc_plttablename);
		Py_DECREF(fc_plttablename);
		pfree(fc_stroid);

		fc_stroid = SPI_getnspname(fc_tdata->tg_relation);
		fc_plttableschema = PLyUnicode_FromString(fc_stroid);
		PyDict_SetItemString(fc_pltdata, "table_schema", fc_plttableschema);
		Py_DECREF(fc_plttableschema);
		pfree(fc_stroid);

		if (TRIGGER_FIRED_BEFORE(fc_tdata->tg_event))
			fc_pltwhen = PLyUnicode_FromString("BEFORE");
		else if (TRIGGER_FIRED_AFTER(fc_tdata->tg_event))
			fc_pltwhen = PLyUnicode_FromString("AFTER");
		else if (TRIGGER_FIRED_INSTEAD(fc_tdata->tg_event))
			fc_pltwhen = PLyUnicode_FromString("INSTEAD OF");
		else
		{
			elog(ERROR, "unrecognized WHEN tg_event: %u", fc_tdata->tg_event);
			fc_pltwhen = NULL;		/* 保持编译器安静 */
		}
		PyDict_SetItemString(fc_pltdata, "when", fc_pltwhen);
		Py_DECREF(fc_pltwhen);

		if (TRIGGER_FIRED_FOR_ROW(fc_tdata->tg_event))
		{
			fc_pltlevel = PLyUnicode_FromString("ROW");
			PyDict_SetItemString(fc_pltdata, "level", fc_pltlevel);
			Py_DECREF(fc_pltlevel);

			/*
			 * Note: In BEFORE trigger, stored generated columns are not
			 * computed yet, so don't make them accessible in NEW row.
			 */

			if (TRIGGER_FIRED_BY_INSERT(fc_tdata->tg_event))
			{
				fc_pltevent = PLyUnicode_FromString("INSERT");

				PyDict_SetItemString(fc_pltdata, "old", Py_None);
				fc_pytnew = PLy_input_from_tuple(&fc_proc->result_in,
											  fc_tdata->tg_trigtuple,
											  fc_rel_descr,
											  !TRIGGER_FIRED_BEFORE(fc_tdata->tg_event));
				PyDict_SetItemString(fc_pltdata, "new", fc_pytnew);
				Py_DECREF(fc_pytnew);
				*fc_rv = fc_tdata->tg_trigtuple;
			}
			else if (TRIGGER_FIRED_BY_DELETE(fc_tdata->tg_event))
			{
				fc_pltevent = PLyUnicode_FromString("DELETE");

				PyDict_SetItemString(fc_pltdata, "new", Py_None);
				fc_pytold = PLy_input_from_tuple(&fc_proc->result_in,
											  fc_tdata->tg_trigtuple,
											  fc_rel_descr,
											  true);
				PyDict_SetItemString(fc_pltdata, "old", fc_pytold);
				Py_DECREF(fc_pytold);
				*fc_rv = fc_tdata->tg_trigtuple;
			}
			else if (TRIGGER_FIRED_BY_UPDATE(fc_tdata->tg_event))
			{
				fc_pltevent = PLyUnicode_FromString("UPDATE");

				fc_pytnew = PLy_input_from_tuple(&fc_proc->result_in,
											  fc_tdata->tg_newtuple,
											  fc_rel_descr,
											  !TRIGGER_FIRED_BEFORE(fc_tdata->tg_event));
				PyDict_SetItemString(fc_pltdata, "new", fc_pytnew);
				Py_DECREF(fc_pytnew);
				fc_pytold = PLy_input_from_tuple(&fc_proc->result_in,
											  fc_tdata->tg_trigtuple,
											  fc_rel_descr,
											  true);
				PyDict_SetItemString(fc_pltdata, "old", fc_pytold);
				Py_DECREF(fc_pytold);
				*fc_rv = fc_tdata->tg_newtuple;
			}
			else
			{
				elog(ERROR, "unrecognized OP tg_event: %u", fc_tdata->tg_event);
				fc_pltevent = NULL;	/* 保持编译器安静 */
			}

			PyDict_SetItemString(fc_pltdata, "event", fc_pltevent);
			Py_DECREF(fc_pltevent);
		}
		else if (TRIGGER_FIRED_FOR_STATEMENT(fc_tdata->tg_event))
		{
			fc_pltlevel = PLyUnicode_FromString("STATEMENT");
			PyDict_SetItemString(fc_pltdata, "level", fc_pltlevel);
			Py_DECREF(fc_pltlevel);

			PyDict_SetItemString(fc_pltdata, "old", Py_None);
			PyDict_SetItemString(fc_pltdata, "new", Py_None);
			*fc_rv = NULL;

			if (TRIGGER_FIRED_BY_INSERT(fc_tdata->tg_event))
				fc_pltevent = PLyUnicode_FromString("INSERT");
			else if (TRIGGER_FIRED_BY_DELETE(fc_tdata->tg_event))
				fc_pltevent = PLyUnicode_FromString("DELETE");
			else if (TRIGGER_FIRED_BY_UPDATE(fc_tdata->tg_event))
				fc_pltevent = PLyUnicode_FromString("UPDATE");
			else if (TRIGGER_FIRED_BY_TRUNCATE(fc_tdata->tg_event))
				fc_pltevent = PLyUnicode_FromString("TRUNCATE");
			else
			{
				elog(ERROR, "unrecognized OP tg_event: %u", fc_tdata->tg_event);
				fc_pltevent = NULL;	/* 保持编译器安静 */
			}

			PyDict_SetItemString(fc_pltdata, "event", fc_pltevent);
			Py_DECREF(fc_pltevent);
		}
		else
			elog(ERROR, "unrecognized LEVEL tg_event: %u", fc_tdata->tg_event);

		if (fc_tdata->tg_trigger->tgnargs)
		{
			/*
			 * all strings...
			 */
			int			fc_i;
			PyObject   *fc_pltarg;

			/* pltargs should have been allocated before the PG_TRY block. */
			Assert(fc_pltargs && fc_pltargs != Py_None);

			for (fc_i = 0; fc_i < fc_tdata->tg_trigger->tgnargs; fc_i++)
			{
				fc_pltarg = PLyUnicode_FromString(fc_tdata->tg_trigger->tgargs[fc_i]);

				/*
				 * stolen, don't Py_DECREF
				 */
				PyList_SetItem(fc_pltargs, fc_i, fc_pltarg);
			}
		}
		else
		{
			Assert(fc_pltargs == Py_None);
		}
		PyDict_SetItemString(fc_pltdata, "args", fc_pltargs);
		Py_DECREF(fc_pltargs);
	}
	PG_CATCH();
	{
		Py_XDECREF(fc_pltargs);
		Py_XDECREF(fc_pltdata);
		PG_RE_THROW();
	}
	PG_END_TRY();

	return fc_pltdata;
}

/*
 * Apply changes requested by a MODIFY return from a trigger function.
 */
static HeapTuple fc_PLy_modify_tuple(PLyProcedure *fc_proc, PyObject *fc_pltd, TriggerData *fc_tdata,
				 HeapTuple fc_otup)
{
	HeapTuple	fc_rtup;
	PyObject   *volatile fc_plntup;
	PyObject   *volatile fc_plkeys;
	PyObject   *volatile fc_plval;
	Datum	   *volatile fc_modvalues;
	bool	   *volatile fc_modnulls;
	bool	   *volatile fc_modrepls;
	ErrorContextCallback fc_plerrcontext;

	fc_plerrcontext.callback = fc_plpython_trigger_error_callback;
	fc_plerrcontext.previous = error_context_stack;
	error_context_stack = &fc_plerrcontext;

	fc_plntup = fc_plkeys = fc_plval = NULL;
	fc_modvalues = NULL;
	fc_modnulls = NULL;
	fc_modrepls = NULL;

	PG_TRY();
	{
		TupleDesc	fc_tupdesc;
		int			fc_nkeys,
					fc_i;

		if ((fc_plntup = PyDict_GetItemString(fc_pltd, "new")) == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("TD[\"new\"] deleted, cannot modify row")));
		Py_INCREF(fc_plntup);
		if (!PyDict_Check(fc_plntup))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("TD[\"new\"] is not a dictionary")));

		fc_plkeys = PyDict_Keys(fc_plntup);
		fc_nkeys = PyList_Size(fc_plkeys);

		fc_tupdesc = RelationGetDescr(fc_tdata->tg_relation);

		fc_modvalues = (Datum *) palloc0(fc_tupdesc->natts * sizeof(Datum));
		fc_modnulls = (bool *) palloc0(fc_tupdesc->natts * sizeof(bool));
		fc_modrepls = (bool *) palloc0(fc_tupdesc->natts * sizeof(bool));

		for (fc_i = 0; fc_i < fc_nkeys; fc_i++)
		{
			PyObject   *fc_platt;
			char	   *fc_plattstr;
			int			fc_attn;
			PLyObToDatum *fc_att;

			fc_platt = PyList_GetItem(fc_plkeys, fc_i);
			if (PyUnicode_Check(fc_platt))
				fc_plattstr = PLyUnicode_AsString(fc_platt);
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", fc_i)));
				fc_plattstr = NULL;	/* 保持编译器安静 */
			}
			fc_attn = SPI_fnumber(fc_tupdesc, fc_plattstr);
			if (fc_attn == SPI_ERROR_NOATTRIBUTE)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
								fc_plattstr)));
			if (fc_attn <= 0)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot set system attribute \"%s\"",
								fc_plattstr)));
			if (TupleDescAttr(fc_tupdesc, fc_attn - 1)->attgenerated)
				ereport(ERROR,
						(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
						 errmsg("cannot set generated column \"%s\"",
								fc_plattstr)));

			fc_plval = PyDict_GetItem(fc_plntup, fc_platt);
			if (fc_plval == NULL)
				elog(FATAL, "Python interpreter is probably corrupted");

			Py_INCREF(fc_plval);

			/* We assume proc->result is set up to convert tuples properly */
			fc_att = &fc_proc->result.u.tuple.atts[fc_attn - 1];

			fc_modvalues[fc_attn - 1] = PLy_output_convert(fc_att,
													 fc_plval,
													 &fc_modnulls[fc_attn - 1]);
			fc_modrepls[fc_attn - 1] = true;

			Py_DECREF(fc_plval);
			fc_plval = NULL;
		}

		fc_rtup = heap_modify_tuple(fc_otup, fc_tupdesc, fc_modvalues, fc_modnulls, fc_modrepls);
	}
	PG_CATCH();
	{
		Py_XDECREF(fc_plntup);
		Py_XDECREF(fc_plkeys);
		Py_XDECREF(fc_plval);

		if (fc_modvalues)
			pfree(fc_modvalues);
		if (fc_modnulls)
			pfree(fc_modnulls);
		if (fc_modrepls)
			pfree(fc_modrepls);

		PG_RE_THROW();
	}
	PG_END_TRY();

	Py_DECREF(fc_plntup);
	Py_DECREF(fc_plkeys);

	pfree(fc_modvalues);
	pfree(fc_modnulls);
	pfree(fc_modrepls);

	error_context_stack = fc_plerrcontext.previous;

	return fc_rtup;
}

static void fc_plpython_trigger_error_callback(void *fc_arg)
{
	PLyExecutionContext *fc_exec_ctx = PLy_current_execution_context();

	if (fc_exec_ctx->curr_proc)
		errcontext("while modifying trigger row");
}

/* execute Python code, propagate Python errors to the backend */
static PyObject * fc_PLy_procedure_call(PLyProcedure *fc_proc, const char *fc_kargs, PyObject *fc_vargs)
{
	PyObject   *fc_rv = NULL;
	int volatile fc_save_subxact_level = list_length(explicit_subtransactions);

	PyDict_SetItemString(fc_proc->globals, fc_kargs, fc_vargs);

	PG_TRY();
	{
#if PY_VERSION_HEX >= 0x03020000
		fc_rv = PyEval_EvalCode(fc_proc->code,
							 fc_proc->globals, fc_proc->globals);
#else
		rv = PyEval_EvalCode((PyCodeObject *) fc_proc->code,
							 fc_proc->globals, fc_proc->globals);
#endif

		/*
		 * Since plpy will only let you close subtransactions that you
		 * started, you cannot *unnest* subtransactions, only *nest* them
		 * without closing.
		 */
		Assert(list_length(explicit_subtransactions) >= fc_save_subxact_level);
	}
	PG_FINALLY();
	{
		fc_PLy_abort_open_subtransactions(fc_save_subxact_level);
	}
	PG_END_TRY();

	/* If the Python code returned an error, propagate it */
	if (fc_rv == NULL)
		PLy_elog(ERROR, NULL);

	return fc_rv;
}

/*
 * Abort lingering subtransactions that have been explicitly started
 * by plpy.subtransaction().start() and not properly closed.
 */
static void fc_PLy_abort_open_subtransactions(int fc_save_subxact_level)
{
	Assert(fc_save_subxact_level >= 0);

	while (list_length(explicit_subtransactions) > fc_save_subxact_level)
	{
		PLySubtransactionData *fc_subtransactiondata;

		Assert(explicit_subtransactions != NIL);

		ereport(WARNING,
				(errmsg("forcibly aborting a subtransaction that has not been exited")));

		RollbackAndReleaseCurrentSubTransaction();

		fc_subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions);
		explicit_subtransactions = list_delete_first(explicit_subtransactions);

		MemoryContextSwitchTo(fc_subtransactiondata->oldcontext);
		CurrentResourceOwner = fc_subtransactiondata->oldowner;
		pfree(fc_subtransactiondata);
	}
}
